﻿// (c) Copyright Fernando Cerqueira.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.ComponentModel;
using System.Windows.Shapes;
using CtrlRes = DevPhone.Controls.LocalizedResources.ControlResources;

// ReSharper disable CheckNamespace
namespace DevPhone.Controls
// ReSharper restore CheckNamespace
{
    public class CalendarControl : Control, ICalendarControlMethods
    {

        #region private Reference template controls

        private StackPanel _monthCalendar;   //view months
        private StackPanel _yearCalendar;    //view years
        private StackPanel _dayCalendar;     //view days (default)

        private Storyboard _showInCalend;    //transition tap month/year
        private Storyboard _showOutCalend;   //transition tap month/year
        private Storyboard _aniShowOut;      //transition flick days
        private Storyboard _aniShowIn;       //transition flick days

        private TextBlock _titleYearGoMonth; //text title go month
        private TextBlock _titleYearGoDay;   //text title go day
        private TextBlock _titleMonthGoYear; //text title go year
        private TextBlock _titleMonthGoDay;  //text title go day
        private TextBlock _todayLnk;         //text title go today
        private TextBlock _monthValue;       //text name current month
        private TextBlock _yearValue;        //text current year
        private TextBlock _titleMonth;       //text title view month
        private TextBlock _titleYear;        //text title view year

        private Border _monthGoDay;          //area tap go month
        private Border _monthGoYear;         //area tap go year
        private Border _yearGoDay;           //area tap go day
        private Border _yearGoMonth;         //area tap go year

        private TextBlock _monthNext;        //text tap next month
        private TextBlock _monthPrev;         //text tap prev month
        private TextBlock _yearNext;         //text tap next year
        private TextBlock _yearPrev;         //text tap prev year



        #endregion

        #region private fields / enuns / class

        //triangle top-right
        private const string MarkTopRight = "M0,0 L100,0 L100,100 L50,50 z";
        //triangle top-right
        private const string MarkTopLeft = "M0,0 L100,0 L50,50 L0,100 z";
        //triangle bottom-left
        private const string MarkBottomLeft = "M0,0 L50,50 L100,100 L0,100 z";
        //triangle bottom-right
        private const string MarkBottomRight = "M50,50 L100,0 L100,100 L0,100 Z";

        private static readonly Brush DefautWeekColor = new SolidColorBrush(Colors.White);
        private static readonly Brush DefautWeekBackground = new SolidColorBrush(Colors.Gray);
        private static readonly Brush DefautBackground = new SolidColorBrush(Color.FromArgb(01, 255, 255, 255));

        //type view Current calendar  
        private enum ActiveShowCalendar
        {
            Day,
            Month,
            Year
        }

        private DateTime _calendarDate;     //date for use day view
        private DateTime _calendarYear;     //date for use year view
        private DateTime _calendarMonth;    //date for use month view

        //culture view
        private CultureInfo _calendarCulture;   

        //flag loaded contrlol
        private bool _isloadedTemplate;

        //list of days to show color mark 
        private readonly List<CalendarColorDay> _colorDays;
        //list of days to disabled
        private readonly List<DateTime?> _disabledDates;

        //active view calandar
        private ActiveShowCalendar _activeShow;
        private ActiveShowCalendar ActiveShow
        {
            get { return _activeShow; }
            set
            {
                _activeShow = value;
                switch (_activeShow)
                {
                    case ActiveShowCalendar.Day:
                        _monthCalendar.Visibility = Visibility.Collapsed;
                        _yearCalendar.Visibility = Visibility.Collapsed;
                        _dayCalendar.Visibility = Visibility.Visible;
                        break;
                    case ActiveShowCalendar.Month:
                        _yearCalendar.Visibility = Visibility.Collapsed;
                        _dayCalendar.Visibility = Visibility.Collapsed;
                        _monthCalendar.Visibility = Visibility.Visible;
                        break;
                    case ActiveShowCalendar.Year:
                        _dayCalendar.Visibility = Visibility.Collapsed;
                        _monthCalendar.Visibility = Visibility.Collapsed;
                        _yearCalendar.Visibility = Visibility.Visible;
                        break;
                }
            }
        }

        //next active view when transition calendar end
        private ActiveShowCalendar _nextActiveShow;

        //flag action new date change by method
        private bool _newDateAction;
        //flag valid direction flick
        private bool _validDirection;
        //flag (1 or -1) direction flick
        private int _valueChange;
        //flag manual Change tap
        private bool _manualTapChange;

        //flick Storyboard Day
        private Storyboard _showDayOutLeft;
        private Storyboard _showDayInLeft;
        private Storyboard _showDayOutRight;
        private Storyboard _showDayInRight;
        private Storyboard _showDayOutUp;
        private Storyboard _showDayInUp;
        private Storyboard _showDayOutDown;
        private Storyboard _showDayInDown;

        //flick Storyboard Month
        private Storyboard _showMonthOutLeft;
        private Storyboard _showMonthInLeft;
        private Storyboard _showMonthOutRight;
        private Storyboard _showMonthInRight;
        private Storyboard _showMonthOutUp;
        private Storyboard _showMonthInUp;
        private Storyboard _showMonthOutDown;
        private Storyboard _showMonthInDown;

        //flick Storyboard Year
        private Storyboard _showYearOutLeft;
        private Storyboard _showYearInLeft;
        private Storyboard _showYearOutRight;
        private Storyboard _showYearInRight;
        private Storyboard _showYearOutUp;
        private Storyboard _showYearInUp;
        private Storyboard _showYearOutDown;
        private Storyboard _showYearInDown;

        #endregion

        #region load / create / template

        public CalendarControl()
        {
            DefaultStyleKey = typeof(CalendarControl);

            _isloadedTemplate = false;
            _colorDays = new List<CalendarColorDay>();
            _disabledDates = new List<DateTime?>();

            IsEnabledChanged += CalendarControlIsEnabledChanged;
        }

        private void CalendarControlIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (!(bool)e.NewValue)
            {
                ActiveShow = ActiveShowCalendar.Day;
            }
            ShowDays(true);
        }

        private void ReadtemplateElements()
        {
            //get controls in template
            _titleMonthGoDay = GetTemplateChild("TitleMonthGoDay") as TextBlock;
            _titleYearGoDay = GetTemplateChild("TitleYearGoDay") as TextBlock;
            _titleMonth = GetTemplateChild("TitleMonth") as TextBlock;
            _titleYear = GetTemplateChild("TitleYear") as TextBlock;
            _monthValue = GetTemplateChild("MonthValue") as TextBlock;
            _yearValue = GetTemplateChild("YearValue") as TextBlock;
            _todayLnk = GetTemplateChild("TodayLnk") as TextBlock;
            _titleMonthGoYear = GetTemplateChild("TitleMonthGoYear") as TextBlock;
            _titleYearGoMonth = GetTemplateChild("TitleYearGoMonth") as TextBlock;
            _monthCalendar = GetTemplateChild("MonthCalendar") as StackPanel;
            _yearCalendar = GetTemplateChild("YearCalendar") as StackPanel;
            _dayCalendar = GetTemplateChild("DayCalendar") as StackPanel;
            _monthGoDay = GetTemplateChild("BMGODAY") as Border;
            _monthGoYear = GetTemplateChild("BMGOYEAR") as Border;
            _yearGoDay = GetTemplateChild("BYGODAY") as Border;
            _yearGoMonth = GetTemplateChild("BYGOMONTH") as Border;

            _monthNext = GetTemplateChild("NextMonthLnk") as TextBlock;
            _monthPrev = GetTemplateChild("PrevMonthLnk") as TextBlock;
            _yearNext = GetTemplateChild("NextYearLnk") as TextBlock;
            _yearPrev = GetTemplateChild("PrevYearLnk") as TextBlock;

            ApplyTitles();
        }

        private void ApplyTitles()
        {
            var curUI = Thread.CurrentThread.CurrentUICulture;
            Thread.CurrentThread.CurrentUICulture = _calendarCulture;

            //Set Titles
            ((TextBlock)GetTemplateChild("TodayLnk")).Text = CustomTitles ? TitleToday :CtrlRes.CalendarControl_TitleToday;

            ((TextBlock)GetTemplateChild("TitleMonth")).Text = CustomTitles ? TitleSelectMonth : CtrlRes.CalendarControl_TitleSelectMonth;
            ((TextBlock)GetTemplateChild("TitleYear")).Text = CustomTitles ? TitleSelectYear : CtrlRes.CalendarControl_TitleSelectYear;

            ((TextBlock)GetTemplateChild("TitleYearGoDay")).Text = CustomTitles ? TitleGoDay : CtrlRes.CalendarControl_TitleGoDay;
            ((TextBlock)GetTemplateChild("TitleYearGoMonth")).Text = CustomTitles ? TitleGoMonth : CtrlRes.CalendarControl_TitleGoMonth;

            ((TextBlock)GetTemplateChild("TitleMonthGoDay")).Text = CustomTitles ? TitleGoDay : CtrlRes.CalendarControl_TitleGoDay;
            ((TextBlock)GetTemplateChild("TitleMonthGoYear")).Text = CustomTitles ? TitleGoYear : CtrlRes.CalendarControl_TitleSelectYear;

            ((TextBlock)GetTemplateChild("NextMonthLnk")).Text = CustomTitles ? TitleNext : CtrlRes.CalendarControl_TitleNext;
            ((TextBlock)GetTemplateChild("PrevMonthLnk")).Text = CustomTitles ? TitlePrevious : CtrlRes.CalendarControl_TitlePrevious;

            ((TextBlock)GetTemplateChild("NextYearLnk")).Text = CustomTitles ? TitleNext : CtrlRes.CalendarControl_TitleNext;
            ((TextBlock)GetTemplateChild("PrevYearLnk")).Text = CustomTitles ? TitlePrevious : CtrlRes.CalendarControl_TitlePrevious;

            Thread.CurrentThread.CurrentUICulture = curUI;
        }

        private void CreateStoryboards()
        {
            //set transition activeview
            _showInCalend = CreateStoryboardChangeViewIn();
            _showOutCalend = CreateStoryboardChangeViewOut();

            //set transition flick day
            _showDayInLeft = CreateStoryboardShowDayInLeft();
            _showDayOutLeft = CreateStoryboardShowDayOutLeft();
            _showDayInRight = CreateStoryboardShowDayInRight();
            _showDayOutRight = CreateStoryboardShowDayOutRight();
            _showDayOutUp = CreateStoryboardShowDayOutUp();
            _showDayInUp = CreateStoryboardShowDayInUp();
            _showDayOutDown = CreateStoryboardShowDayOutDown();
            _showDayInDown = CreateStoryboardShowDayInDown();

            //set transition flick month
            _showMonthInLeft = CreateStoryboardShowMonthInLeft();
            _showMonthOutLeft = CreateStoryboardShowMonthOutLeft();
            _showMonthInRight = CreateStoryboardShowMonthInRight();
            _showMonthOutRight = CreateStoryboardShowMonthOutRight();
            _showMonthOutUp = CreateStoryboardShowMonthOutUp();
            _showMonthInUp = CreateStoryboardShowMonthInUp();
            _showMonthOutDown = CreateStoryboardShowMonthOutDown();
            _showMonthInDown = CreateStoryboardShowMonthInDown();

            //set transition flick year
            _showYearInLeft = CreateStoryboardShowYearInLeft();
            _showYearOutLeft = CreateStoryboardShowYearOutLeft();
            _showYearInRight = CreateStoryboardShowYearInRight();
            _showYearOutRight = CreateStoryboardShowYearOutRight();
            _showYearOutUp = CreateStoryboardShowYearOutUp();
            _showYearInUp = CreateStoryboardShowYearInUp();
            _showYearOutDown = CreateStoryboardShowYearOutDown();
            _showYearInDown = CreateStoryboardShowYearInDown();

            _showOutCalend.Completed += ShowOutCalendOnCompleted;

        }

        private void CreateEventsElements()
        {
            if (_todayLnk != null) _todayLnk.Tap += TodayLnkTap;
            if (_monthValue != null) _monthValue.Tap += MonthValueTap;
            if (_yearValue != null) _yearValue.Tap += YearValueTap;
            if (_monthGoDay != null) _monthGoDay.Tap += BmgodayTap;
            if (_monthGoYear != null) _monthGoYear.Tap += BmgoyearTap;
            if (_yearGoDay != null) _yearGoDay.Tap += BygodayTap;
            if (_yearGoMonth != null) _yearGoMonth.Tap += BygomonthTap;

            _monthNext.Tap += MonthNextOnTap;
            _monthPrev.Tap += MonthPrevOnTap;
            _yearNext.Tap += YearNextOnTap;
            _yearPrev.Tap += YearPrevOnTap;

            //set days events tap
            for (var i = 1; i <= 35; i++)
            {
                var bday = (Border)GetTemplateChild("BD" + i.ToString(CultureInfo.InvariantCulture));
                bday.Tap += DayTap;
            }
            //set months events tap
            for (var i = 1; i <= 12; i++)
            {
                var bmonth = (Border)GetTemplateChild("BM" + i.ToString(CultureInfo.InvariantCulture));
                bmonth.Tap += MonthTap;
            }
            //set years events tap
            for (var i = 0; i < 15; i++)
            {
                var byear = (Border)GetTemplateChild("BY" + i.ToString(CultureInfo.InvariantCulture));
                byear.Tap += YearTap;
            }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            //Set Culture
            _calendarCulture = string.IsNullOrEmpty(Culture) ? Thread.CurrentThread.CurrentUICulture : new CultureInfo(Culture);

            ReadtemplateElements();

            CreateStoryboards();

            ClipBoundsControl.SetToBounds(GetTemplateChild("AreaDays") as StackPanel, true);
            ClipBoundsControl.SetToBounds(GetTemplateChild("AreaMonths") as StackPanel, true);
            ClipBoundsControl.SetToBounds(GetTemplateChild("AreaYears") as StackPanel, true);

            CreateEventsElements();

            //default values
            if (WeekBrush == null)
            {
                WeekBrush = DefautWeekColor;
            }
            if (WeekBackground == null)
            {
                WeekBackground = DefautWeekBackground;
            }
            if (CalendarForeground == null || Foreground == null)
            {
                CalendarForeground = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
            }
            if (CalendarBorderBrush == null || BorderBrush == null)
            {
                CalendarBorderBrush = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
            }
            if (CalendarBackground == null || Background == null || Background == new SolidColorBrush(Colors.Transparent))
            {
                CalendarBackground = DefautBackground;
            }
            if (CurrentDateBrush == null)
            {
                CurrentDateBrush = (Brush)Application.Current.Resources["PhoneAccentBrush"];
            }
            if (TodayBrush == null)
            {
                TodayBrush = (Brush)Application.Current.Resources["PhoneContrastBackgroundBrush"];
            }
            if (DisabledBrush == null)
            {
                DisabledBrush = (Brush)Application.Current.Resources["PhoneDisabledBrush"];
            }

            //Default ActiveShow
            ActiveShow = ActiveShowCalendar.Day;
            _nextActiveShow = ActiveShowCalendar.Day;

            //Set Current Date
            if (!CurrentDate.HasValue)
            {
                CurrentDate = DateTime.Now.Date;
            }
            _calendarDate = CurrentDate.Value.Date;

            //show week and days
            ColorWeek();
            ShowWeek();
            ShowDays(true);

            // Translate it into view once layout has been updated
            Dispatcher.BeginInvoke(() =>
            {
                ManipulationCompleted += CalendarControlManipulationCompleted;
                _isloadedTemplate = true;
                CompositionTarget.Rendering += DoWorkOnRender;
            });

        }

        private void DoWorkOnRender(object sender, EventArgs args)
        {
            CompositionTarget.Rendering -= DoWorkOnRender;
            OnExportMethods(new ExportInterfaceEventArgs<ICalendarControlMethods>(this));
        }

        #endregion

        #region Calendar Events

        public event EventHandler<CalendarChangeSelectDateEventArgs> ChangeSelectDate;
        public event EventHandler<ExportInterfaceEventArgs<ICalendarControlMethods>> ExportMethods;
        public event EventHandler<CalendarChangeEventArgs> ChangeCalendar;

        #endregion

        #region Calendar Handles events

        private void OnChangeCalendar(CalendarChangeEventArgs e)
        {
            var handler = ChangeCalendar;
            if (handler != null) handler(this, e);
        }

        private void OnSeletedDate(CalendarChangeSelectDateEventArgs e)
        {
            var handler = ChangeSelectDate;
            if (handler != null) handler(this, e);
        }

        private void OnExportMethods(ExportInterfaceEventArgs<ICalendarControlMethods> e)
        {
            var handler = ExportMethods;
            if (handler != null) handler(this, e);
        }

        #endregion

        #region Storyboards events

        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Animate logic")]
        private void AniShowOutCompleted(object sender, EventArgs e)
        {
            switch (ActiveShow)
            {
                case ActiveShowCalendar.Day:
                    if (!_newDateAction)
                    {
                        var olddate = _calendarDate;
                        var newdate = _calendarDate.AddMonths(_valueChange);
                        var arg = new CalendarChangeEventArgs(olddate, newdate);
                        OnChangeCalendar(arg);

                        var dtref = new DateTime(arg.Year, arg.Month, 1);

                        if (MinDate.HasValue)
                        {
                            if (dtref < MinDate.Value.Date && dtref.AddMonths(1).AddDays(-1) < MinDate.Value.Date)
                            {
                                if (LoopRangeMinMax && MaxDate.HasValue)
                                {
                                    dtref = new DateTime(MaxDate.Value.Year, MaxDate.Value.Month, 1);
                                }
                                else
                                {
                                    dtref = _calendarDate;
                                }
                            }
                        }

                        if (MaxDate.HasValue)
                        {
                            if (dtref > MaxDate.Value.Date)
                            {
                                if (LoopRangeMinMax && MinDate.HasValue)
                                {
                                    dtref = new DateTime(MinDate.Value.Year, MinDate.Value.Month, 1);
                                }
                                else
                                {
                                    dtref = _calendarDate;
                                }
                            }
                        }

                        _calendarDate = dtref;

                    }
                    ShowDays(false);
                    _newDateAction = false;
                    break;
                case ActiveShowCalendar.Year:
                    {
                        var dtref = _calendarYear.AddYears(_valueChange * 15);
                        var dtini = dtref.AddYears(-7);
                        var dtend = dtref.AddYears(7);

                        if (MinDate.HasValue)
                        {
                            if (dtini.Year < MinDate.Value.Year && dtend.Year < MinDate.Value.Date.Year)
                            {
                                if (LoopRangeMinMax && MaxDate.HasValue)
                                {
                                    dtref = new DateTime(MaxDate.Value.Year, MaxDate.Value.Month, 1);
                                    dtini = dtref.AddYears(-7);
                                    dtend = dtref.AddYears(7);
                                    if (dtini.Year < MinDate.Value.Year && dtend.Year > MaxDate.Value.Year)
                                    {
                                        dtref = _calendarYear;
                                    }
                                }
                                else
                                {
                                    dtref = _calendarYear;
                                }
                            }
                        }
                        if (MaxDate.HasValue)
                        {
                            if (dtini.Year > MaxDate.Value.Date.Year && dtend.Year > MaxDate.Value.Year)
                            {
                                if (LoopRangeMinMax && MinDate.HasValue)
                                {
                                    dtref = new DateTime(MinDate.Value.Year, MinDate.Value.Month, 1);
                                    dtini = dtref.AddYears(-7);
                                    dtend = dtref.AddYears(7);
                                    if (dtini.Year < MinDate.Value.Year && dtend.Year > MaxDate.Value.Year)
                                    {
                                        dtref = _calendarYear;
                                    }
                                }
                                else
                                {
                                    dtref = _calendarYear;
                                }
                            }
                        }
                        _calendarYear = dtref;
                        ShowYears();
                    }
                    break;
                case ActiveShowCalendar.Month:
                    {
                        var dtref = _calendarMonth.AddYears(_valueChange);
                        var dtaux = new DateTime(dtref.Year, 1, 1);
                        if (MinDate.HasValue)
                        {
                            if (dtaux < new DateTime(MinDate.Value.Year, MinDate.Value.Month, 1) && dtaux.AddMonths(11) < new DateTime(MinDate.Value.Year, MinDate.Value.Month, 1))
                            {
                                if (LoopRangeMinMax && MaxDate.HasValue)
                                {
                                    dtref = new DateTime(MaxDate.Value.Year, MaxDate.Value.Month, 1);
                                }
                                else
                                {
                                    dtref = _calendarMonth;
                                }
                            }
                        }
                        if (MaxDate.HasValue)
                        {
                            if (dtaux.AddMonths(11) > new DateTime(MaxDate.Value.Year, MaxDate.Value.Month, 1) && dtaux > new DateTime(MaxDate.Value.Year, MaxDate.Value.Month, 1))
                            {
                                if (LoopRangeMinMax && MinDate.HasValue)
                                {
                                    dtref = new DateTime(MinDate.Value.Year, MinDate.Value.Month, 1);
                                }
                                else
                                {
                                    dtref = _calendarMonth;
                                }
                            }
                        }

                        _calendarMonth = dtref;
                        ShowMonths();
                    }
                    break;
            }
            if (_aniShowIn != null)
            {
                _aniShowIn.Begin();
            }
        }

        private void CalendarControlManipulationCompleted(object sender, System.Windows.Input.ManipulationCompletedEventArgs e)
        {
            e.Handled = true;
            if (GestureDirection == CalendarGestureDirection.None)
            {
                return;
            }

            //direction flick
            var x = e.TotalManipulation.Translation.X;
            var y = e.TotalManipulation.Translation.Y;
            if (x < 0)
            {
                x = x * -1;
            }
            if (y < 0)
            {
                y = y * -1;
            }

            _validDirection = false;
            _valueChange = 0;
            switch (GestureDirection)
            {
                case CalendarGestureDirection.Vertical:
                    _validDirection = (x < y);
                    _valueChange = e.TotalManipulation.Translation.Y > 0 ? -1 : 1;
                    break;
                case CalendarGestureDirection.Horizontal:
                    _validDirection = (x > y);
                    _valueChange = e.TotalManipulation.Translation.X > 0 ? -1 : 1;
                    break;
            }

            if (_validDirection)
            {
                ShowAniTransitionDate();
            }
        }

        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Animate logic")]
        private void ShowAniTransitionDate()
        {
            if (_aniShowIn != null)
            {
                _aniShowIn.Stop();
            }
            if (_aniShowOut != null)
            {
                _aniShowOut.Stop();
                _aniShowOut.Completed -= AniShowOutCompleted;
            }

            if (_valueChange < 0)
            {
                switch (GestureDirection)
                {
                    case CalendarGestureDirection.None:
                        {
                            if (_manualTapChange)
                            {
                                _manualTapChange = false;
                                switch (ActiveShow)
                                {
                                    case ActiveShowCalendar.Year:
                                        _aniShowOut = _showYearOutDown;
                                        _aniShowIn = _showYearInDown;
                                        break;
                                    case ActiveShowCalendar.Month:
                                        _aniShowOut = _showMonthOutDown;
                                        _aniShowIn = _showMonthInDown;
                                        break;
                                    case ActiveShowCalendar.Day:
                                        _aniShowOut = _showDayOutDown;
                                        _aniShowIn = _showDayInDown;
                                        break;
                                }
                            }
                        }
                        break;
                    case CalendarGestureDirection.Vertical:
                        switch (ActiveShow)
                        {
                            case ActiveShowCalendar.Year:
                                _aniShowOut = _showYearOutDown;
                                _aniShowIn = _showYearInDown;
                                break;
                            case ActiveShowCalendar.Month:
                                _aniShowOut = _showMonthOutDown;
                                _aniShowIn = _showMonthInDown;
                                break;
                            case ActiveShowCalendar.Day:
                                _aniShowOut = _showDayOutDown;
                                _aniShowIn = _showDayInDown;
                                break;
                        }
                        break;
                    case CalendarGestureDirection.Horizontal:
                        switch (ActiveShow)
                        {
                            case ActiveShowCalendar.Year:
                                _aniShowOut = _showYearOutRight;
                                _aniShowIn = _showYearInRight;
                                break;
                            case ActiveShowCalendar.Month:
                                _aniShowOut = _showMonthOutRight;
                                _aniShowIn = _showMonthInRight;
                                break;
                            case ActiveShowCalendar.Day:
                                _aniShowOut = _showDayOutRight;
                                _aniShowIn = _showDayInRight;
                                break;
                        }
                        break;
                }
            }
            else
            {
                switch (GestureDirection)
                {
                    case CalendarGestureDirection.None:
                        {
                            if (_manualTapChange)
                            {
                                _manualTapChange = false;
                            }
                            switch (ActiveShow)
                            {
                                case ActiveShowCalendar.Year:
                                    _aniShowOut = _showYearOutUp;
                                    _aniShowIn = _showYearInUp;
                                    break;
                                case ActiveShowCalendar.Month:
                                    _aniShowOut = _showMonthOutUp;
                                    _aniShowIn = _showMonthInUp;
                                    break;
                                case ActiveShowCalendar.Day:
                                    _aniShowOut = _showDayOutUp;
                                    _aniShowIn = _showDayInUp;
                                    break;
                            }
                        }
                        break;
                    case CalendarGestureDirection.Vertical:
                        switch (ActiveShow)
                        {
                            case ActiveShowCalendar.Year:
                                _aniShowOut = _showYearOutUp;
                                _aniShowIn = _showYearInUp;
                                break;
                            case ActiveShowCalendar.Month:
                                _aniShowOut = _showMonthOutUp;
                                _aniShowIn = _showMonthInUp;
                                break;
                            case ActiveShowCalendar.Day:
                                _aniShowOut = _showDayOutUp;
                                _aniShowIn = _showDayInUp;
                                break;
                        }
                        break;
                    case CalendarGestureDirection.Horizontal:
                        switch (ActiveShow)
                        {
                            case ActiveShowCalendar.Year:
                                _aniShowOut = _showYearOutLeft;
                                _aniShowIn = _showYearInLeft;
                                break;
                            case ActiveShowCalendar.Month:
                                _aniShowOut = _showMonthOutLeft;
                                _aniShowIn = _showMonthInLeft;
                                break;
                            case ActiveShowCalendar.Day:
                                _aniShowOut = _showDayOutLeft;
                                _aniShowIn = _showDayInLeft;
                                break;
                        }
                        break;
                }
            }
            if (_aniShowOut != null) _aniShowOut.Completed += AniShowOutCompleted;

            ClearViewdDecorate(ActiveShow);

            if (_aniShowOut != null) _aniShowOut.Begin();
        }

        private void ClearViewdDecorate(ActiveShowCalendar show)
        {
            switch (show)
            {
                case ActiveShowCalendar.Day:
                    for (var i = 1; i <= 35; i++)
                    {
                        var bday = (Border)GetTemplateChild("BD" + i.ToString(CultureInfo.InvariantCulture));
                        var day = (TextBlock)GetTemplateChild("D" + i.ToString(CultureInfo.InvariantCulture));
                        var gday = (Grid)GetTemplateChild("GD" + i.ToString(CultureInfo.InvariantCulture));
                        ClearDecorate(day, gday,bday);
                    }
                    break;
                case ActiveShowCalendar.Month:
                    for (var i = 1; i <= 12; i++)
                    {
                        var month = (TextBlock)GetTemplateChild("M" + i.ToString(CultureInfo.InvariantCulture));
                        ClearDecorate(month, null,null);
                    }
                    break;
                case ActiveShowCalendar.Year:
                    for (var i = 0; i < 15; i++)
                    {
                        var year = (TextBlock)GetTemplateChild("Y" + i.ToString(CultureInfo.InvariantCulture));
                        ClearDecorate(year, null, null);
                    }
                    break;
            }
        }

        private void ShowOutCalendOnCompleted(object sender, EventArgs eventArgs)
        {
            ActiveShow = _nextActiveShow;
            _nextActiveShow = ActiveShow;
            switch (ActiveShow)
            {
                case ActiveShowCalendar.Day:
                    ShowDays(true);
                    break;
                case ActiveShowCalendar.Month:
                    _calendarMonth = _calendarDate;
                    ShowMonths();
                    break;
                case ActiveShowCalendar.Year:
                    _calendarYear = _calendarDate;
                    ShowYears();
                    break;
            }
            _showInCalend.Begin();
        }

        #endregion

        #region private  methods

        private int PosWeek(DateTime value)
        {
            return ((int)(new DateTime(value.Year, value.Month, value.Day, _calendarCulture.Calendar)).DayOfWeek);
        }

        private static int PosDayControl(int newpos)
        {
            return newpos > 35 ? 35 : newpos;
        }

        private void ClearDecorate(TextBlock textBlock, Grid grid, Border border)
        {
            if (textBlock != null)
            {
                textBlock.Foreground = Foreground;
                textBlock.TextDecorations = null;
            }
            if (border != null)
            {
                border.BorderBrush = BorderBrush;
                border.Background = Background;
                if (grid != null && grid.Tag != null)
                {
                    border.BorderThickness = (Thickness)grid.Tag;
                }
            }
            if (grid != null)
            {
                var itemspath = grid.Children.Where(c => c as Path != null).ToArray();
                foreach (var item in itemspath)
                {
                    grid.Children.Remove(item);
                }
            }
        }

        private static Brush ConvertToBrush(object value)
        {
            var newvalue = value as Brush;
            if (newvalue == null)
            {
                throw new InvalidCastException("value must be Brush type");
            }
            return newvalue;
        }

        #endregion

        #region storyboads

        private Storyboard CreateStoryboardChangeViewIn()
        {
            var sb = new Storyboard();

            var edkf1 = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(new TimeSpan()), Value = 270 };
            var edkf2 = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200)), Value = 360 };
            var dakf1 = new DoubleAnimationUsingKeyFrames();
            dakf1.KeyFrames.Add(edkf1);
            dakf1.KeyFrames.Add(edkf2);
            Storyboard.SetTargetProperty(dakf1, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)"));
            Storyboard.SetTarget(dakf1, _dayCalendar);
            sb.Children.Add(dakf1);

            var edkf3 = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(new TimeSpan()), Value = 270 };
            var edkf4 = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200)), Value = 360 };
            var dakf2 = new DoubleAnimationUsingKeyFrames();
            dakf2.KeyFrames.Add(edkf3);
            dakf2.KeyFrames.Add(edkf4);
            Storyboard.SetTargetProperty(dakf2, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)"));
            Storyboard.SetTarget(dakf2, _monthCalendar);
            sb.Children.Add(dakf2);

            var edkf5 = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(new TimeSpan()), Value = 270 };
            var edkf6 = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200)), Value = 360 };
            var dakf3 = new DoubleAnimationUsingKeyFrames();
            dakf3.KeyFrames.Add(edkf5);
            dakf3.KeyFrames.Add(edkf6);
            Storyboard.SetTargetProperty(dakf3, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)"));
            Storyboard.SetTarget(dakf3, _yearCalendar);
            sb.Children.Add(dakf3);

            return sb;
        }

        private Storyboard CreateStoryboardChangeViewOut()
        {
            var sb = new Storyboard();

            var edkf1 = new EasingDoubleKeyFrame {KeyTime = KeyTime.FromTimeSpan(new TimeSpan()), Value = 0};
            var edkf2 = new EasingDoubleKeyFrame {KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200)), Value = 90};
            var dakf1 = new DoubleAnimationUsingKeyFrames();

            dakf1.KeyFrames.Add(edkf1);
            dakf1.KeyFrames.Add(edkf2);
            Storyboard.SetTargetProperty(dakf1, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)"));
            Storyboard.SetTarget(dakf1, _dayCalendar);
            sb.Children.Add(dakf1);

            var edkf3 = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(new TimeSpan()), Value = 0 };
            var edkf4 = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200)), Value = 90 };
            var dakf2 = new DoubleAnimationUsingKeyFrames();
            dakf2.KeyFrames.Add(edkf3);
            dakf2.KeyFrames.Add(edkf4);
            Storyboard.SetTargetProperty(dakf2, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)"));
            Storyboard.SetTarget(dakf2, _monthCalendar);
            sb.Children.Add(dakf2);

            var edkf5 = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(new TimeSpan()), Value = 0 };
            var edkf6 = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200)), Value = 90 };
            var dakf3 = new DoubleAnimationUsingKeyFrames();
            dakf3.KeyFrames.Add(edkf5);
            dakf3.KeyFrames.Add(edkf6);
            Storyboard.SetTargetProperty(dakf3, new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)"));
            Storyboard.SetTarget(dakf3, _yearCalendar);
            sb.Children.Add(dakf3);

            return sb;
        }

        private Storyboard CreateStoryboardShowDayOutLeft()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 35; i++)
            {
                var day = (TextBlock)GetTemplateChild("D" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = -455 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowDayInLeft()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 35; i++)
            {
                var day = (TextBlock)GetTemplateChild("D" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 455, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowDayOutRight()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 35; i++)
            {
                var day = (TextBlock)GetTemplateChild("D" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = 455 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowDayInRight()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 35; i++)
            {
                var day = (TextBlock)GetTemplateChild("D" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = -455, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowDayOutUp()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 35; i++)
            {
                var day = (TextBlock)GetTemplateChild("D" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = -325 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowDayInUp()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 35; i++)
            {
                var day = (TextBlock)GetTemplateChild("D" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 325, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowDayOutDown()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 35; i++)
            {
                var day = (TextBlock)GetTemplateChild("D" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = 325 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowDayInDown()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 35; i++)
            {
                var day = (TextBlock)GetTemplateChild("D" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = -325, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowMonthOutLeft()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 12; i++)
            {
                var day = (TextBlock)GetTemplateChild("M" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = -455 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowMonthInLeft()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 12; i++)
            {
                var day = (TextBlock)GetTemplateChild("M" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 455, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowMonthOutRight()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 12; i++)
            {
                var day = (TextBlock)GetTemplateChild("M" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = 455 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowMonthInRight()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 12; i++)
            {
                var day = (TextBlock)GetTemplateChild("M" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = -455, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowMonthOutUp()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 12; i++)
            {
                var day = (TextBlock)GetTemplateChild("M" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = -325 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowMonthInUp()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 12; i++)
            {
                var day = (TextBlock)GetTemplateChild("M" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 325, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowMonthOutDown()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 12; i++)
            {
                var day = (TextBlock)GetTemplateChild("M" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = 325 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowMonthInDown()
        {
            var sb = new Storyboard();
            for (var i = 1; i <= 12; i++)
            {
                var day = (TextBlock)GetTemplateChild("M" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = -325, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowYearOutLeft()
        {
            var sb = new Storyboard();
            for (var i = 0; i < 15; i++)
            {
                var day = (TextBlock)GetTemplateChild("Y" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = -455 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowYearInLeft()
        {
            var sb = new Storyboard();
            for (var i = 0; i < 15; i++)
            {
                var day = (TextBlock)GetTemplateChild("Y" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 455, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowYearOutRight()
        {
            var sb = new Storyboard();
            for (var i = 0; i < 15; i++)
            {
                var day = (TextBlock)GetTemplateChild("Y" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = 455 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowYearInRight()
        {
            var sb = new Storyboard();
            for (var i = 0; i < 15; i++)
            {
                var day = (TextBlock)GetTemplateChild("Y" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = -455, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetX)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowYearOutUp()
        {
            var sb = new Storyboard();
            for (var i = 0; i < 15; i++)
            {
                var day = (TextBlock)GetTemplateChild("Y" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = -325 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowYearInUp()
        {
            var sb = new Storyboard();
            for (var i = 0; i < 15; i++)
            {
                var day = (TextBlock)GetTemplateChild("Y" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 325, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowYearOutDown()
        {
            var sb = new Storyboard();
            for (var i = 0; i < 15; i++)
            {
                var day = (TextBlock)GetTemplateChild("Y" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = 0, To = 325 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        private Storyboard CreateStoryboardShowYearInDown()
        {
            var sb = new Storyboard();
            for (var i = 0; i < 15; i++)
            {
                var day = (TextBlock)GetTemplateChild("Y" + i.ToString(CultureInfo.InvariantCulture));
                var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(200)), From = -325, To = 0 };
                Storyboard.SetTarget(da, day);
                Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Projection).(PlaneProjection.GlobalOffsetY)"));
                sb.Children.Add(da);
            }
            return sb;
        }

        #endregion

        #region show week/Day/month/year

        private void ColorWeek()
        {
            if (WeekBackground != null)
            {
                for (var i = 1; i <= 7; i++)
                {
                    var bw = GetTemplateChild("BT" + i.ToString(CultureInfo.InvariantCulture)) as Border;
                    if (bw != null) bw.Background = WeekBackground;
                }
            }
            if (WeekBrush != null)
            {
                for (var i = 1; i <= 7; i++)
                {
                    var tw = GetTemplateChild("T" + i.ToString(CultureInfo.InvariantCulture)) as TextBlock;
                    if (tw != null) tw.Foreground = WeekBrush;
                }
            }
        }

        private void ShowWeek()
        {
            var names = _calendarCulture.DateTimeFormat.AbbreviatedDayNames;
            for (var i = 1; i <= 7; i++)
            {
                var tw = GetTemplateChild("T" + i.ToString(CultureInfo.InvariantCulture)) as TextBlock;
                if (tw != null) tw.Text = names[i - 1];
            }
        }

        private void ShowMonths()
        {
            _titleMonthGoYear.Foreground = !EnabledTapShowYear ? DisabledBrush : WeekBrush;

            if (GestureDirection == CalendarGestureDirection.None)
            {
                _monthNext.Visibility = Visibility.Visible;
                _monthPrev.Visibility = Visibility.Visible;
            }
            else
            {
                _monthNext.Visibility = Visibility.Collapsed;
                _monthPrev.Visibility = Visibility.Collapsed;
            }

            var currentMonth = _calendarDate;

            for (var i = 1; i <= 12; i++)
            {
                var month = (TextBlock)GetTemplateChild("M" + i.ToString(CultureInfo.InvariantCulture));
                var bmonth = (Border)GetTemplateChild("BM" + i.ToString(CultureInfo.InvariantCulture));

                month.Foreground = Foreground;
                bmonth.Background = Background;
                if (i == currentMonth.Month && _calendarMonth.Year == _calendarDate.Year)
                {
                    month.Foreground = TodayBrush ?? Foreground;
                }

                month.Text = _calendarCulture.DateTimeFormat.AbbreviatedMonthNames[i - 1] + "\n" + _calendarMonth.Year.ToString(CultureInfo.InvariantCulture);
                bmonth.Tag = i.ToString(CultureInfo.InvariantCulture) + ";" + _calendarMonth.Year.ToString(CultureInfo.InvariantCulture);

                var monthyeaardisabled = false;
                if (MinDate.HasValue)
                {
                    if (new DateTime(_calendarMonth.Year, i, 1) < new DateTime(MinDate.Value.Year, MinDate.Value.Month, 1))
                    {
                        monthyeaardisabled = true;
                    }
                }
                if (MaxDate.HasValue)
                {
                    if (new DateTime(_calendarMonth.Year, i, 1) > new DateTime(MaxDate.Value.Year, MaxDate.Value.Month, 1))
                    {
                        monthyeaardisabled = true;
                    }
                }
                if (monthyeaardisabled)
                {
                    if (DisabledBrush != null)
                    {
                        month.Foreground = DisabledBrush;
                    }
                }

                bmonth.IsHitTestVisible = (!monthyeaardisabled);
            }
        }

        private void ShowYears()
        {
            _titleYearGoMonth.Foreground = !EnabledTapShowMonth ? DisabledBrush : WeekBrush;

            if (GestureDirection == CalendarGestureDirection.None)
            {
                _yearNext.Visibility = Visibility.Visible;
                _yearPrev.Visibility = Visibility.Visible;
            }
            else
            {
                _yearNext.Visibility = Visibility.Collapsed;
                _yearPrev.Visibility = Visibility.Collapsed;
            }

            var yearini = _calendarYear.Year - 7;
            for (var i = 0; i < 15; i++)
            {
                var year = (TextBlock)GetTemplateChild("Y" + i.ToString(CultureInfo.InvariantCulture));
                var byear = (Border)GetTemplateChild("BY" + i.ToString(CultureInfo.InvariantCulture));

                year.Foreground = Foreground;
                byear.Background = Background;

                year.Text = (yearini + i).ToString(CultureInfo.InvariantCulture);
                byear.Tag = (yearini + i).ToString(CultureInfo.InvariantCulture);

                if (int.Parse(year.Text, CultureInfo.InvariantCulture) == _calendarDate.Year)
                {
                    year.Foreground = TodayBrush ?? Foreground;
                }

                var yeaardisabled = false;
                if (MinDate.HasValue)
                {
                    if (new DateTime((yearini + i), 1, 1) < new DateTime(MinDate.Value.Year, 1, 1))
                    {
                        yeaardisabled = true;
                    }
                }
                if (MaxDate.HasValue)
                {
                    if (new DateTime((yearini + i), 1, 1) > new DateTime(MaxDate.Value.Year, 1, 1))
                    {
                        yeaardisabled = true;
                    }
                }
                if (yeaardisabled)
                {
                    if (DisabledBrush != null)
                    {
                        year.Foreground = DisabledBrush;
                    }
                }

                byear.IsHitTestVisible = (!yeaardisabled);

            }
        }

        private void ShowDays(bool clearDescorate)
        {

            var dtini = new DateTime(_calendarDate.Year, _calendarDate.Month, 1);
            var dtend = dtini.AddMonths(1).AddDays(-1);
            var posini = PosWeek(dtini);

            _todayLnk.Foreground = !IsEnabled ? DisabledBrush : Foreground;
            _todayLnk.Visibility = ShowLinkToday && IsEnabled ? Visibility.Visible : Visibility.Collapsed;

            for (var i = 1; i <= 35; i++)
            {
                var day = (TextBlock)GetTemplateChild("D" + i.ToString(CultureInfo.InvariantCulture));
                var bday = (Border)GetTemplateChild("BD" + i.ToString(CultureInfo.InvariantCulture));
                var gday = (Grid)GetTemplateChild("GD" + i.ToString(CultureInfo.InvariantCulture));

                var index = PosDayControl(dtini.Day + posini);

                if (clearDescorate || !IsEnabled)
                {
                    ClearDecorate(day, gday,bday);
                }
                gday.Tag = null;
                day.Text = string.Empty;
                bday.Tag = null;
                bday.IsHitTestVisible = false;
                if (i >= (index) && dtini <= dtend)
                {
                    day.Text = dtini.Day.ToString(CultureInfo.InvariantCulture);
                    bday.Tag = dtini;
                    if (IsEnabled)
                    {
                        if (dtini.Date == DateTime.Now.Date)
                        {
                            _todayLnk.Visibility = Visibility.Collapsed;
                        }
                        // ReSharper disable PossibleInvalidOperationException
                        if (dtini.Date == CurrentDate.Value.Date)
                        // ReSharper restore PossibleInvalidOperationException
                        {
                            day.Foreground = CurrentDateBrush ?? Foreground;
                        }

                        ShowColorday(dtini, gday);
                        ShowHighlightToday(dtini, day, gday, bday);
                        ShowDisabledDay(dtini, day, bday);
                    }
                    else
                    {
                        day.Foreground = DisabledBrush;
                    }
                    
                    dtini = dtini.AddDays(1);
                }
            }
            _monthValue.Text = _calendarCulture.DateTimeFormat.MonthNames[_calendarDate.Month - 1];
            _yearValue.Text = _calendarDate.Year.ToString(CultureInfo.InvariantCulture);
            _monthValue.Foreground = !IsEnabled ? DisabledBrush : Foreground;
            _yearValue.Foreground = !IsEnabled ? DisabledBrush : Foreground;

        }

        private void ShowDisabledDay(DateTime value,TextBlock day,Border bday)
        {
            DateTime? dd = null;
            if (MinDate.HasValue)
            {
                if (value.Date < MinDate.Value)
                {
                    dd = value;
                }
            }
            if (MaxDate.HasValue)
            {
                if (value.Date > MaxDate.Value)
                {
                    dd = value;
                }
            }
            if (dd == null)
            {
                if (_disabledDates.Count > 0)
                {
                    dd = _disabledDates.FirstOrDefault(c => c.HasValue && c.Value == value.Date);
                }
            }

            bday.IsHitTestVisible = (dd == null);

            if (dd != null && DisabledBrush != null)
            {
                day.Foreground = DisabledBrush;
            }
        }

        private  void ShowColorday(DateTime value,Grid gday)
        {
            if (_colorDays.Count > 0)
            {
                var dc = _colorDays.FirstOrDefault(c => c.Date.Date == value.Date);
                if (dc != null)
                {
                    var dw = dc.Width;
                    if (dc.Width == CalendarMarkWidth.Default)
                    {
                        dw = MarkWidth == CalendarMarkWidth.Default ? CalendarMarkWidth.Small : MarkWidth;
                    }
                    switch (MarkPosition)
                    {
                        case CalendarPositionMark.TopRight:
                            gday.Children.Add(CreateMarkTopRight((Brush)dc.ColorBrushMark, dw));
                            break;
                        case CalendarPositionMark.TopLeft:
                            gday.Children.Add(CreateMarkTopLeft((Brush)dc.ColorBrushMark, dw));
                            break;
                        case CalendarPositionMark.BottomRight:
                            gday.Children.Add(CreateMarkBottomRight((Brush)dc.ColorBrushMark, dw));
                            break;
                        case CalendarPositionMark.BottomLeft:
                            gday.Children.Add(CreateMarkBottomLeft((Brush)dc.ColorBrushMark, dw));
                            break;
                    }
                }
            }
        }

        private void ShowHighlightTodayMarkDate(DateTime value, Grid gday)
        {
            var dc = _colorDays.FirstOrDefault(d => d.Date == value.Date);
            var redraw = false;
            var tmw = dc != null ? dc.Width : MarkWidth;
            switch (tmw)
            {
                case CalendarMarkWidth.Large:
                    tmw = CalendarMarkWidth.Medium;
                    break;
                case CalendarMarkWidth.Medium:
                    tmw = CalendarMarkWidth.Small;
                    break;
                case CalendarMarkWidth.Default:
                case CalendarMarkWidth.Small:
                    if (dc != null)
                    {
                        redraw = true;
                    }
                    tmw = CalendarMarkWidth.Medium;
                    break;
            }
            switch (MarkPosition)
            {
                case CalendarPositionMark.TopRight:
                    gday.Children.Add(CreateMarkTopRight(TodayBrush, tmw));
                    if (redraw)
                    {
                        gday.Children.Add(dc.Width == CalendarMarkWidth.Default
                                              ? CreateMarkTopRight((Brush)dc.ColorBrushMark, CalendarMarkWidth.Small)
                                              : CreateMarkTopRight((Brush)dc.ColorBrushMark, dc.Width));
                    }
                    break;
                case CalendarPositionMark.TopLeft:
                    gday.Children.Add(CreateMarkTopLeft(TodayBrush, tmw));
                    if (redraw)
                    {
                        gday.Children.Add(dc.Width == CalendarMarkWidth.Default
                                              ? CreateMarkTopLeft((Brush)dc.ColorBrushMark, CalendarMarkWidth.Small)
                                              : CreateMarkTopLeft((Brush)dc.ColorBrushMark, dc.Width));
                    }
                    break;
                case CalendarPositionMark.BottomRight:
                    gday.Children.Add(CreateMarkBottomRight(TodayBrush, tmw));
                    if (redraw)
                    {
                        gday.Children.Add(dc.Width == CalendarMarkWidth.Default
                                              ? CreateMarkBottomRight((Brush)dc.ColorBrushMark,
                                                                      CalendarMarkWidth.Small)
                                              : CreateMarkBottomRight((Brush)dc.ColorBrushMark, dc.Width));
                    }
                    break;
                case CalendarPositionMark.BottomLeft:
                    gday.Children.Add(CreateMarkBottomLeft(TodayBrush, tmw));
                    if (redraw)
                    {
                        gday.Children.Add(dc.Width == CalendarMarkWidth.Default
                                              ? CreateMarkBottomLeft((Brush)dc.ColorBrushMark,
                                                                     CalendarMarkWidth.Small)
                                              : CreateMarkBottomLeft((Brush)dc.ColorBrushMark, dc.Width));
                    }
                    break;
            }
        }

        private void ShowHighlightToday(DateTime value,TextBlock day,Grid gday, Border bday)
        {
            if (value.Date != DateTime.Now.Date || TodayHighlight == CalendarHighlightToday.None) return;
            switch (TodayHighlight)
            {
                case CalendarHighlightToday.Underline:
                    day.TextDecorations = TextDecorations.Underline;
                    break;
                case CalendarHighlightToday.Background:
                    bday.Background = TodayBrush;
                    break;
                case CalendarHighlightToday.Border:
                    bday.BorderBrush = TodayBrush;
                    gday.Tag = bday.BorderThickness;
                    bday.BorderThickness = new Thickness(3);
                    break;
                case CalendarHighlightToday.MarkDate:
                    ShowHighlightTodayMarkDate(value, gday);
                    break;
            }
        }

        #endregion

        #region ghost Dependency Properties

        public static readonly DependencyProperty CalendarBorderBrushProperty =
        DependencyProperty.Register("CalendarBorderBrush", typeof(Brush), typeof(CalendarControl), new PropertyMetadata(null, CalendarBorderBrushChangedCallback));

        private static void CalendarBorderBrushChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (e.NewValue == null)
            {
                var defcor = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
                instance.CalendarBorderBrush = defcor;
            }
            instance.BorderBrush = instance.CalendarBorderBrush;
            if (instance._isloadedTemplate)
            {
                switch (instance.ActiveShow)
                {
                    case ActiveShowCalendar.Day:
                        instance.ShowWeek();
                        instance.ShowDays(true);
                        break;
                    case ActiveShowCalendar.Month:
                        instance.ShowMonths();
                        break;
                    case ActiveShowCalendar.Year:
                        instance.ShowYears();
                        break;
                }
            }
        }

        public Brush CalendarBorderBrush
        {
            get { return (Brush)GetValue(CalendarBorderBrushProperty); }
            set
            {
                SetValue(CalendarBorderBrushProperty, value);
                SetValue(BorderBrushProperty, value);
            }
        }

        public static readonly DependencyProperty CalendarBackgroundProperty =
                DependencyProperty.Register("CalendarBackground", typeof(Brush), typeof(CalendarControl), new PropertyMetadata(null, CalendarBackgroundChangedCallback));

        private static void CalendarBackgroundChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.CalendarBackground = DefautBackground;
            }
            instance.Background = instance.CalendarBackground;
            if (instance._isloadedTemplate)
            {
                switch (instance.ActiveShow)
                {
                    case ActiveShowCalendar.Day:
                        instance.ShowWeek();
                        instance.ShowDays(true);
                        break;
                    case ActiveShowCalendar.Month:
                        instance.ShowMonths();
                        break;
                    case ActiveShowCalendar.Year:
                        instance.ShowYears();
                        break;
                }
            }
        }

        public Brush CalendarBackground
        {
            get { return (Brush)GetValue(CalendarBackgroundProperty); }
            set
            {
                SetValue(CalendarBackgroundProperty, value);
                SetValue(BackgroundProperty, value);
            }
        }

        public static readonly DependencyProperty CalendarForegroundProperty =
                DependencyProperty.Register("CalendarForeground", typeof(Brush), typeof(CalendarControl), new PropertyMetadata(null, CalendarForegroundChangedCallback));

        private static void CalendarForegroundChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (e.NewValue == null)
            {
                var defcor = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
                instance.CalendarForeground = defcor;
            }
            instance.Foreground = instance.CalendarForeground;
            if (instance._isloadedTemplate)
            {
                switch (instance.ActiveShow)
                {
                    case ActiveShowCalendar.Day:
                        instance.ShowWeek();
                        instance.ShowDays(true);
                        break;
                    case ActiveShowCalendar.Month:
                        instance.ShowMonths();
                        break;
                    case ActiveShowCalendar.Year:
                        instance.ShowYears();
                        break;
                }
            }
        }

        public Brush CalendarForeground
        {
            get { return (Brush)GetValue(CalendarForegroundProperty); }
            set
            {
                SetValue(CalendarForegroundProperty, value);
                SetValue(ForegroundProperty, value);
            }
        }

        #endregion

        #region Dependency Properties

        public static readonly DependencyProperty CustomTitlesProperty =
            DependencyProperty.Register("CustomTitles", typeof(bool), typeof(CalendarControl), new PropertyMetadata(false, CustomTitlesChangedCallback));

        private static void CustomTitlesChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                switch (instance.ActiveShow)
                {
                    case ActiveShowCalendar.Day:
                        instance.ShowWeek();
                        instance.ShowDays(true);
                        break;
                    case ActiveShowCalendar.Month:
                        instance.ShowMonths();
                        break;
                    case ActiveShowCalendar.Year:
                        instance.ShowYears();
                        break;
                }
            }
        }

        public bool CustomTitles
        {
            get { return (bool)GetValue(CustomTitlesProperty); }
            set
            {
                SetValue(CustomTitlesProperty, value);
            }
        }

        public static readonly DependencyProperty CultureProperty =
                DependencyProperty.Register("Culture", typeof(string), typeof(CalendarControl), new PropertyMetadata(null, CultureChangedCallback));

        private static void CultureChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            instance._calendarCulture = string.IsNullOrEmpty((string)e.NewValue) ? Thread.CurrentThread.CurrentUICulture : new CultureInfo((string)e.NewValue);
            if (instance._isloadedTemplate)
            {
                switch (instance.ActiveShow)
                {
                    case ActiveShowCalendar.Day:
                        instance.ShowWeek();
                        instance.ShowDays(true);
                        break;
                    case ActiveShowCalendar.Month:
                        instance.ShowMonths();
                        break;
                    case ActiveShowCalendar.Year:
                        instance.ShowYears();
                        break;
                }
            }
        }

        public string Culture
        {
            get { return (string)GetValue(CultureProperty); }
            set
            {
                SetValue(CultureProperty, value);
                if (_isloadedTemplate)
                {
                    ApplyTitles();
                }
            }
        }

        public static readonly DependencyProperty CurrentDateProperty =
                DependencyProperty.Register("CurrentDate", typeof(DateTime?), typeof(CalendarControl), new PropertyMetadata(null, CurrentDateChangedCallback));


        private static void CurrentDateChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            var dtref = DateTime.Now.Date;
            if (instance.CurrentDate != null)
            {
                dtref = instance.CurrentDate.Value.Date;
            }
            if (instance._isloadedTemplate)
            {
                instance.ChangeDate(dtref);
            }
        }

        [TypeConverter(typeof(NullableDateTimeTypeConverter))]
        public DateTime? CurrentDate
        {
            get { return (DateTime?)GetValue(CurrentDateProperty); }
            set
            {
                SetValue(CurrentDateProperty, value);
            }
        }

        public static readonly DependencyProperty CurrentDateBrushProperty =
                DependencyProperty.Register("CurrentDateBrush", typeof(Brush), typeof(CalendarControl), new PropertyMetadata(null, CurrentDateBrushChangedCallback));

        private static void CurrentDateBrushChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.CurrentDateBrush = (Brush)Application.Current.Resources["PhoneAccentBrush"];
            }
            if (instance._isloadedTemplate && instance.ActiveShow == ActiveShowCalendar.Day)
            {
                instance.ShowDays(true);
            }
        }

        public Brush CurrentDateBrush
        {
            get { return (Brush)GetValue(CurrentDateBrushProperty); }
            set
            {
                SetValue(CurrentDateBrushProperty, value);
            }
        }

        public static readonly DependencyProperty TodayBrushProperty =
                DependencyProperty.Register("TodayBrush", typeof(Brush), typeof(CalendarControl), new PropertyMetadata(null, TodayBrushChangedCallback));

        private static void TodayBrushChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.TodayBrush = (Brush)Application.Current.Resources["PhoneContrastBackgroundBrush"];
            }
            if (instance._isloadedTemplate && instance.ActiveShow == ActiveShowCalendar.Day)
            {
                instance.ShowDays(true);
            }
        }

        public Brush TodayBrush
        {
            get { return (Brush)GetValue(TodayBrushProperty); }
            set
            {
                SetValue(TodayBrushProperty, value);
            }
        }

        public static readonly DependencyProperty WeekBrushProperty =
                DependencyProperty.Register("WeekBrush", typeof(Brush), typeof(CalendarControl), new PropertyMetadata(null, WeekBrushChangedCallback));

        private static void WeekBrushChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.WeekBrush = DefautWeekColor;
            }
            if (instance._isloadedTemplate)
            {
                instance.ColorWeek();
            }
        }

        public Brush WeekBrush
        {
            get { return (Brush)GetValue(WeekBrushProperty); }
            set
            {
               SetValue(WeekBrushProperty, value);
            }
        }

        public static readonly DependencyProperty WeekBackgroundProperty =
                DependencyProperty.Register("WeekBackground", typeof(Brush), typeof(CalendarControl), new PropertyMetadata(null, WeekBackgroundChangedCallback));

        private static void WeekBackgroundChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.WeekBackground = DefautWeekBackground;
            }
            if (instance._isloadedTemplate)
            {
                instance.ColorWeek();
            }
        }

        public Brush WeekBackground
        {
            get { return (Brush)GetValue(WeekBackgroundProperty); }
            set
            {
                SetValue(WeekBackgroundProperty, value);
            }
        }

        public static readonly DependencyProperty MarkPositionProperty =
                DependencyProperty.Register("MarkPosition", typeof(CalendarPositionMark), typeof(CalendarControl), new PropertyMetadata(CalendarPositionMark.TopRight,MarkPositionChangedCallback));

        private static void MarkPositionChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate && instance.ActiveShow == ActiveShowCalendar.Day)
            {
                instance.ShowDays(true);
            }
        }

        public CalendarPositionMark MarkPosition
        {
            get { return (CalendarPositionMark)GetValue(MarkPositionProperty); }
            set
            {
                SetValue(MarkPositionProperty, value);
            }
        }

        public static readonly DependencyProperty MarkWidthProperty =
                DependencyProperty.Register("MarkWidth", typeof(CalendarMarkWidth), typeof(CalendarControl), new PropertyMetadata(CalendarMarkWidth.Default, MarkWidthChangedCallback));

        private static void MarkWidthChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate && instance.ActiveShow == ActiveShowCalendar.Day)
            {
                instance.ShowDays(true);
            }
        }

        public CalendarMarkWidth MarkWidth
        {
            get { return (CalendarMarkWidth)GetValue(MarkWidthProperty); }
            set
            {
                SetValue(MarkWidthProperty, value);
            }
        }

        public static readonly DependencyProperty GestureDirectionProperty =
                DependencyProperty.Register("GestureDirection", typeof(CalendarGestureDirection), typeof(CalendarControl), new PropertyMetadata(CalendarGestureDirection.None));

        public CalendarGestureDirection GestureDirection
        {
            get { return (CalendarGestureDirection)GetValue(GestureDirectionProperty); }
            set
            {
                SetValue(GestureDirectionProperty, value);
            }
        }

        public static readonly DependencyProperty EnabledTapShowMonthProperty =
        DependencyProperty.Register("EnabledTapShowMonth", typeof(bool), typeof(CalendarControl), new PropertyMetadata(true));

        public bool EnabledTapShowMonth
        {
            get { return (bool)GetValue(EnabledTapShowMonthProperty); }
            set
            {
                SetValue(EnabledTapShowMonthProperty, value);
            }
        }

        public static readonly DependencyProperty EnabledTapShowYearProperty =
            DependencyProperty.Register("EnabledTapShowYear", typeof(bool), typeof(CalendarControl), new PropertyMetadata(true));

        public bool EnabledTapShowYear
        {
            get { return (bool)GetValue(EnabledTapShowYearProperty); }
            set
            {
                SetValue(EnabledTapShowYearProperty, value);
            }
        }

        public static readonly DependencyProperty DisabledBrushProperty =
                DependencyProperty.Register("DisabledBrush", typeof(Brush), typeof(CalendarControl), new PropertyMetadata(null, DisabledBrushChangedCallback));

        private static void DisabledBrushChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.DisabledBrush = (Brush)Application.Current.Resources["PhoneDisabledBrush"];
            }
            if (instance._isloadedTemplate)
            {
                switch (instance.ActiveShow)
                {
                    case ActiveShowCalendar.Day:
                        instance.ShowDays(true);
                        break;
                    case ActiveShowCalendar.Month:
                        instance.ShowMonths();
                        break;
                    case ActiveShowCalendar.Year:
                        instance.ShowYears();
                        break;
                }
            }
        }

        public Brush DisabledBrush
        {
            get { return (Brush)GetValue(DisabledBrushProperty); }
            set
            {
                SetValue(DisabledBrushProperty, value);
            }
        }

        public static readonly DependencyProperty MaxDateProperty =
        DependencyProperty.Register("MaxDate", typeof(DateTime?), typeof(CalendarControl), new PropertyMetadata(null, MaxDateChangedCallback));

        private static void MaxDateChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                switch (instance.ActiveShow)
                {
                    case ActiveShowCalendar.Day:
                        instance.ShowDays(true);
                        break;
                    case ActiveShowCalendar.Month:
                        instance.ShowMonths();
                        break;
                    case ActiveShowCalendar.Year:
                        instance.ShowYears();
                        break;
                }
            }
        }

        
        [TypeConverter(typeof(NullableDateTimeTypeConverter))]
        public DateTime? MaxDate
        {
            get { return (DateTime?)GetValue(MaxDateProperty); }
            set
            {
                SetValue(MaxDateProperty, value);
            }
        }

        public static readonly DependencyProperty MinDateProperty =
        DependencyProperty.Register("MinDate", typeof(DateTime?), typeof(CalendarControl), new PropertyMetadata(null, MinDateChangedCallback));

        private static void MinDateChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                switch (instance.ActiveShow)
                {
                    case ActiveShowCalendar.Day:
                        instance.ShowDays(true);
                        break;
                    case ActiveShowCalendar.Month:
                        instance.ShowMonths();
                        break;
                    case ActiveShowCalendar.Year:
                        instance.ShowYears();
                        break;
                }
            }
        }

        [TypeConverter(typeof(NullableDateTimeTypeConverter))]
        public DateTime? MinDate
        {
            get { return (DateTime?)GetValue(MinDateProperty); }
            set
            {
                SetValue(MinDateProperty, value);
            }
        }

        public static readonly DependencyProperty ShowLinkTodayProperty =
            DependencyProperty.Register("ShowLinkToday", typeof(bool), typeof(CalendarControl), new PropertyMetadata(true, ShowLinkTodayChangedCallback));

        private static void ShowLinkTodayChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                if (instance.ActiveShow == ActiveShowCalendar.Day)
                {
                    instance.ShowDays(false);
                }
            }
        }

        public bool ShowLinkToday
        {
            get { return (bool)GetValue(ShowLinkTodayProperty); }
            set
            {
                SetValue(ShowLinkTodayProperty, value);
            }
        }

        public static readonly DependencyProperty TodayHighlightProperty =
            DependencyProperty.Register("TodayHighlight", typeof(CalendarHighlightToday), typeof(CalendarControl), new PropertyMetadata(CalendarHighlightToday.None, TodayHighlightChangedCallback));

        private static void TodayHighlightChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                if (instance.ActiveShow == ActiveShowCalendar.Day)
                {
                    instance.ShowDays(true);
                }
            }
        }

        public CalendarHighlightToday TodayHighlight
        {
            get { return (CalendarHighlightToday)GetValue(TodayHighlightProperty); }
            set
            {
                SetValue(TodayHighlightProperty, value);
            }
        }

        public static readonly DependencyProperty LoopRangeMinMaxProperty =
            DependencyProperty.Register("LoopRangeMinMax", typeof(bool), typeof(CalendarControl), new PropertyMetadata(true));

        public bool LoopRangeMinMax
        {
            get { return (bool)GetValue(LoopRangeMinMaxProperty); }
            set
            {
                SetValue(LoopRangeMinMaxProperty, value);
            }
        }

        public static readonly DependencyProperty TitleSelectMonthProperty =
            DependencyProperty.Register("TitleSelectMonth", typeof(string), typeof(CalendarControl), new PropertyMetadata("Months", TitleSelectMonthChangedCallback));

        private static void TitleSelectMonthChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                instance._titleMonth.Text = (string)e.NewValue;
            }
        }

        public string TitleSelectMonth
        {
            get { return (string)GetValue(TitleSelectMonthProperty); }
            set
            {
                SetValue(TitleSelectMonthProperty, value);
            }
        }

        public static readonly DependencyProperty TitleSelectYearProperty =
            DependencyProperty.Register("TitleSelectYear", typeof(string), typeof(CalendarControl), new PropertyMetadata("Years", TitleSelectYearChangedCallback));

        private static void TitleSelectYearChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                instance._titleYear.Text = (string)e.NewValue;
            }
        }

        public string TitleSelectYear
        {
            get { return (string)GetValue(TitleSelectYearProperty); }
            set
            {
                SetValue(TitleSelectYearProperty, value);
            }
        }

        public static readonly DependencyProperty TitleGoDayProperty =
            DependencyProperty.Register("TitleGoDay", typeof(string), typeof(CalendarControl), new PropertyMetadata("Days", TitleGoDayChangedCallback));

        private static void TitleGoDayChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                instance._titleYearGoDay.Text = (string)e.NewValue;
                instance._titleMonthGoDay.Text = (string)e.NewValue;
            }
        }

        public string TitleGoDay
        {
            get { return (string)GetValue(TitleGoDayProperty); }
            set
            {
                SetValue(TitleGoDayProperty, value);
            }
        }

        public static readonly DependencyProperty TitleGoMonthProperty =
            DependencyProperty.Register("TitleGoMonth", typeof(string), typeof(CalendarControl), new PropertyMetadata("Months", TitleGoMonthChangedCallback));

        private static void TitleGoMonthChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                instance._titleYearGoMonth.Text = (string)e.NewValue;
            }
        }

        public string TitleGoMonth
        {
            get { return (string)GetValue(TitleGoMonthProperty); }
            set
            {
                SetValue(TitleGoMonthProperty, value);
            }
        }

        public static readonly DependencyProperty TitleGoYearProperty =
            DependencyProperty.Register("TitleGoYear", typeof(string), typeof(CalendarControl), new PropertyMetadata("Years", TitleGoYearChangedCallback));

        private static void TitleGoYearChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                instance._titleMonthGoYear.Text = (string)e.NewValue;
            }
        }

        public string TitleGoYear
        {
            get { return (string)GetValue(TitleGoYearProperty); }
            set
            {
                SetValue(TitleGoYearProperty, value);
            }
        }

        public static readonly DependencyProperty TitleTodayProperty =
            DependencyProperty.Register("TitleToday", typeof(string), typeof(CalendarControl), new PropertyMetadata("Today", TitleTodayChangedCallback));

        private static void TitleTodayChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                instance._todayLnk.Text = (string)e.NewValue;
            }
        }

        public string TitleToday
        {
            get { return (string)GetValue(TitleTodayProperty); }
            set
            {
                SetValue(TitleTodayProperty, value);
            }
        }

        public static readonly DependencyProperty TitleNextProperty =
            DependencyProperty.Register("TitleNext", typeof(string), typeof(CalendarControl), new PropertyMetadata("Next", TitleNextChangedCallback));

        private static void TitleNextChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                instance._monthNext.Text = (string)e.NewValue;
                instance._yearNext.Text = (string)e.NewValue;
            }
        }

        public string TitleNext
        {
            get { return (string)GetValue(TitleNextProperty); }
            set
            {
                SetValue(TitleNextProperty, value);
            }
        }

        public static readonly DependencyProperty TitlePreviousProperty =
            DependencyProperty.Register("TitlePrevious", typeof(string), typeof(CalendarControl), new PropertyMetadata("Prev", TitlePreviousChangedCallback));

        private static void TitlePreviousChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CalendarControl)dependencyObject;
            if (instance._isloadedTemplate)
            {
                instance._monthPrev.Text = (string)e.NewValue;
                instance._yearPrev.Text = (string)e.NewValue;
            }
        }

        public string TitlePrevious
        {
            get { return (string)GetValue(TitlePreviousProperty); }
            set
            {
                SetValue(TitlePreviousProperty, value);
            }
        }

        #endregion

        #region Day/month/year tap events

        private void DayTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            var dtsel = (DateTime)((Border)sender).Tag;
            OnSeletedDate(new CalendarChangeSelectDateEventArgs(dtsel));
        }

        private void MonthTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            var monthYearSel = ((string)((Border)sender).Tag).Split(';');
            _calendarDate = new DateTime(int.Parse(monthYearSel[1], CultureInfo.InvariantCulture), int.Parse(monthYearSel[0], CultureInfo.InvariantCulture), _calendarDate.Day);
            _nextActiveShow = ActiveShowCalendar.Day;
            _showOutCalend.Begin();
        }

        private void YearTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            var yearSel = (string)((Border)sender).Tag;

            var dtref = new DateTime(int.Parse(yearSel, CultureInfo.InvariantCulture), _calendarDate.Month, _calendarDate.Day);
            if (MinDate.HasValue)
            {
                if (dtref < MinDate.Value.Date && dtref.AddMonths(1).AddDays(-1) < MinDate.Value.Date)
                {
                    dtref = MinDate.Value.Date;
                }
            }

            if (MaxDate.HasValue)
            {
                if (dtref > MaxDate.Value.Date)
                {
                    if (LoopRangeMinMax && MinDate.HasValue)
                    {
                        dtref = MaxDate.Value.Date;
                    }
                }
            }
            _calendarDate = dtref;
            _nextActiveShow = ActiveShowCalendar.Day;
            _showOutCalend.Begin();
        }

        private void MonthValueTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            if (!EnabledTapShowMonth) return;
            _nextActiveShow = ActiveShowCalendar.Month;
            _showOutCalend.Begin();
        }

        private void YearValueTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            if (!EnabledTapShowYear)
            {
                if (EnabledTapShowMonth)
                {
                    _nextActiveShow = ActiveShowCalendar.Month;
                    _showOutCalend.Begin();
                }
                return;
            }
            _nextActiveShow = ActiveShowCalendar.Year;
            _showOutCalend.Begin();
        }

        private void BmgodayTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            _nextActiveShow = ActiveShowCalendar.Day;
            _showOutCalend.Begin();
        }

        private void BmgoyearTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            if (EnabledTapShowYear)
            {
                _nextActiveShow = ActiveShowCalendar.Year;
                _showOutCalend.Begin();
            }
        }

        private void BygodayTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            _nextActiveShow = ActiveShowCalendar.Day;
            _showOutCalend.Begin();
        }

        private void BygomonthTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            if (EnabledTapShowMonth)
            {
                _nextActiveShow = ActiveShowCalendar.Month;
                _showOutCalend.Begin();
            }
        }

        private void YearPrevOnTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            _validDirection = true;
            _valueChange = -1;
            ActiveShow = ActiveShowCalendar.Year;
            _nextActiveShow = ActiveShowCalendar.Year;
            _manualTapChange = true;
            ShowAniTransitionDate();
        }

        private void YearNextOnTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            _validDirection = true;
            _valueChange = 1;
            ActiveShow = ActiveShowCalendar.Year;
            _nextActiveShow = ActiveShowCalendar.Year;
            _manualTapChange = true;
            ShowAniTransitionDate();
        }

        private void MonthPrevOnTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            _validDirection = true;
            _valueChange = -1;
            ActiveShow = ActiveShowCalendar.Month;
            _nextActiveShow = ActiveShowCalendar.Month;
            _manualTapChange = true;
            ShowAniTransitionDate();
        }

        private void MonthNextOnTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            _validDirection = true;
            _valueChange = 1;
            ActiveShow = ActiveShowCalendar.Month;
            _nextActiveShow = ActiveShowCalendar.Month;
            _manualTapChange = true;
            ShowAniTransitionDate();
        }

        #endregion

        #region Create PathGeometric Marks

        private Path CreateMarkBottomRight(Brush color, CalendarMarkWidth markwdith)
        {

            return new Path
            {
                Data = StringPathGeometry.Convert(MarkBottomRight),
                Fill = color,
                Stretch = Stretch.UniformToFill,
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Bottom,
                Width = (double)markwdith,
                Height = (double)markwdith,
                Stroke = BorderBrush,
                StrokeThickness = 1,
                Margin = new Thickness(0)
            };
        }

        private Path CreateMarkBottomLeft(Brush color, CalendarMarkWidth markwdith)
        {

            return new Path
            {
                Data = StringPathGeometry.Convert(MarkBottomLeft),
                Fill = color,
                Stretch = Stretch.UniformToFill,
                HorizontalAlignment = HorizontalAlignment.Left,
                VerticalAlignment = VerticalAlignment.Bottom,
                Width = (double)markwdith,
                Height = (double)markwdith,
                Stroke = BorderBrush,
                StrokeThickness = 1
            };
        }

        private Path CreateMarkTopLeft(Brush color, CalendarMarkWidth markwdith)
        {

            return new Path
            {
                Data = StringPathGeometry.Convert(MarkTopLeft),
                Fill = color,
                Stretch = Stretch.UniformToFill,
                HorizontalAlignment = HorizontalAlignment.Left,
                VerticalAlignment = VerticalAlignment.Top,
                Width = (double)markwdith,
                Height = (double)markwdith,
                Stroke = BorderBrush,
                StrokeThickness = 1
            };
        }

        private Path CreateMarkTopRight(Brush color, CalendarMarkWidth markwdith)
        {

            return new Path
            {
                Data = StringPathGeometry.Convert(MarkTopRight),
                Fill = color,
                Stretch = Stretch.UniformToFill,
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Top,
                Width = (double)markwdith,
                Height = (double)markwdith,
                Stroke = BorderBrush,
                StrokeThickness = 1
            };
        }

        #endregion

        #region Interface ICalendarControlMethods

        private CalendarColorDay ExistColorday(DateTime value)
        {
            return _colorDays.FirstOrDefault(c => c.Date.Date == value.Date);
        }

        public void ChangeDate(DateTime value)
        {
            _validDirection = (_calendarDate.Date != value.Date);
            _valueChange = 0;
            if (_validDirection)
            {
                ActiveShow = ActiveShowCalendar.Day;
                _nextActiveShow = ActiveShowCalendar.Day;
                _newDateAction = true;
                _valueChange = _calendarDate.Date > value.Date ? -1 : 1;
                _calendarDate = value.Date;
                ShowAniTransitionDate();
            }
        }

        public void AddDateColor(DateTime value, object colorBrush)
        {
            AddDateColor(value, ConvertToBrush(colorBrush), MarkWidth);
        }

        public void AddDateColor(DateTime value, object colorBrush, CalendarMarkWidth width)
        {
            var item = ExistColorday(value);
            if (item != null)
            {
                var index = _colorDays.IndexOf(item);
                _colorDays[index].ColorBrushMark = ConvertToBrush(colorBrush);
                _colorDays[index].Width = width;
            }
            else
            {
                _colorDays.Add(new CalendarColorDay
                {
                    Date = value.Date,
                    ColorBrushMark = colorBrush,
                    Width = width
                });
            }
            if (_isloadedTemplate && ActiveShow == ActiveShowCalendar.Day)
            {
                ShowDays(true);
            }
        }

        public void RemoveDateColor(DateTime value)
        {
            var item = ExistColorday(value);
            if (item != null)
            {
                _colorDays.Remove(item);
                if (_isloadedTemplate &&  ActiveShow == ActiveShowCalendar.Day)
                {
                    ShowDays(true);
                }
            }
        }

        public void ClearDateColor()
        {
            if (_colorDays.Count > 0)
            {
                _colorDays.Clear();
                if (_isloadedTemplate && ActiveShow == ActiveShowCalendar.Day)
                {
                    ShowDays(true);
                }
            }
        }

        public CalendarColorDay[] ColorDays()
        {
            return _colorDays.ToArray();
        }

        private DateTime? ExistDisabledDate(DateTime value)
        {
            return _disabledDates.FirstOrDefault(c => c.HasValue && c.Value == value.Date);
        }

        public void AddDisabledDate(DateTime value)
        {
            if (ExistDisabledDate(value) == null)
            {
                _disabledDates.Add(value);
                if (_isloadedTemplate && ActiveShow == ActiveShowCalendar.Day)
                {
                    ShowDays(true);
                }
            }
        }

        public void RemoveDisabledDate(DateTime value)
        {
            var result = ExistDisabledDate(value);
            if (result != null)
            {
                _disabledDates.Remove(result);
                if (_isloadedTemplate && ActiveShow == ActiveShowCalendar.Day)
                {
                    ShowDays(true);
                }
            }
        }

        public void ClearDisabledDate()
        {
            if (_disabledDates.Count > 0)
            {
                var result = DisabledDates();
                foreach (var id in result.Select(ExistDisabledDate))
                {
                    _disabledDates.Remove(id);
                }
                if (_isloadedTemplate && ActiveShow == ActiveShowCalendar.Day)
                {
                    ShowDays(true);
                }
            }
        }

        public DateTime[] DisabledDates()
        {
            // ReSharper disable PossibleInvalidOperationException
            return _disabledDates.Where(c => c.HasValue).Select(c => c.Value).ToArray();
            // ReSharper restore PossibleInvalidOperationException
        }

        private void TodayLnkTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            _calendarDate = DateTime.Now.Date;
            _nextActiveShow = ActiveShowCalendar.Day;
            _showOutCalend.Begin();
        }

        #endregion

    }
}
